#pragma once
#include <iostream>
#include <string>
#include <unordered_map>
#include <memory>
#include <mutex>
#include <cassert>
#include "../mqcommom/helper.hpp"

using namespace ns_helper;
namespace ns_rabbitmq
{
    class Binding
    {
    public:
        Binding(const std::string &exchange_name, const std::string &queue_name, const std::string &binding_key, bool durable)
            : _exchange_name(exchange_name), _queue_name(queue_name), _binding_key(binding_key), _durable(durable)
        {
        }

        // Binding() {}

    public:
        std::string _exchange_name;
        std::string _queue_name;
        std::string _binding_key;
        bool _durable;
    };

    using BindingPtr = std::shared_ptr<Binding>;
    // 一个交换机可能有多个队列，一个队列对应一个交换机
    // queuename,queueptr
    using QueueMap = std::unordered_map<std::string, std::shared_ptr<Binding>>;
    // exchange,queuemap
    using BindingMap = std::unordered_map<std::string, QueueMap>;
    class BindingMapper
    {
    public:
        BindingMapper(const std::string &dbfile)
            : _sqlite_helper(dbfile)
        {
            // 1.获取父路径
            std::string path = FileHelper::ParentDirctory(dbfile);
            // 2.创建路径
            FileHelper::CreateDirctory(path);
            // 3.创建数据库
            assert(_sqlite_helper.Open());
            CreateTable();
        }

        void CreateTable()
        {
            std::string sql = "create table if not exists binding_table (";
            sql += "exchange_name varchar(32),queue_name varchar(32),binding_key vrachar(128),";
            sql += "durable int);";
            assert(_sqlite_helper.Exec(sql, nullptr, nullptr));
        }

        void RemoveTable()
        {
            std::string sql = "drop table if exists binding_table;";
            _sqlite_helper.Exec(sql, nullptr, nullptr);
        }
        // 新增insert
        bool Insert(BindingPtr &binding)
        {
            std::stringstream sql;
            sql << "insert into binding_table values(";
            sql << "'" << binding->_exchange_name << "',";
            sql << "'" << binding->_queue_name << "',";
            sql << "'" << binding->_binding_key << "',";
            sql << binding->_durable << ");";
            return _sqlite_helper.Exec(sql.str(), nullptr, nullptr);
        }

        void Remove(const std::string &ename, const std::string &qname)
        {
            std::stringstream sql;
            sql << "delete from binding_table where ";
            sql << "exchange_name='" << ename << "' and ";
            sql << "queue_name='" << qname << "';";
            _sqlite_helper.Exec(sql.str(), nullptr, nullptr);
        }

        void RemoveExchangeBindings(const std::string &exchange_name)
        {
            std::string sql = "delete from binding_table where exchange_name='";
            sql += exchange_name + "';";
            _sqlite_helper.Exec(sql, nullptr, nullptr);
        }

        void RemoveQueueBindings(const std::string &queue_name)
        {
            std::string sql = "delete from binding_table where queue_name='";
            sql += queue_name + "';";
            _sqlite_helper.Exec(sql, nullptr, nullptr);
        }

        BindingMap Recovery()
        {
            BindingMap bindmap;
            std::string sql = "select * from binding_table;";
            _sqlite_helper.Exec(sql, RecoveryCallback, &bindmap);
            return bindmap;
        }

    private:
        static int RecoveryCallback(void *args, int numcol, char **row, char **fields)
        {
            BindingMap *bindmap = static_cast<BindingMap *>(args);
            // 构造出一个队列
            BindingPtr bindptr = std::make_shared<Binding>(row[0], row[1], row[2], (bool)row[3]);
            QueueMap &quemap = (*bindmap)[bindptr->_exchange_name];
            quemap.insert(std::make_pair(bindptr->_queue_name, bindptr));
            return 0;
        }
        SqliteHelper _sqlite_helper;
    };

    class BindManager
    {
    public:
        using BindingManagePtr = std::shared_ptr<BindManager>;
        BindManager(const std::string &dbfile)
            : _mapper(dbfile)
        {
            _bindings = _mapper.Recovery();
        }

        bool Bind(const std::string &exchange_name, const std::string &queue_name, const std::string &binding_key, bool durable)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto eit = _bindings.find(exchange_name);
            if (eit != _bindings.end() && eit->second.find(queue_name) != eit->second.end())
                return true;
            // 构建绑定
            auto bindingptr = std::make_shared<Binding>(exchange_name, queue_name, binding_key, durable);
            if (durable)
                _mapper.Insert(bindingptr);
            // 引用，没有就创建
            auto &qmap = _bindings[exchange_name];
            qmap.insert(std::make_pair(queue_name, bindingptr));
            return true;
        }

        void UnBind(const std::string &exchange_name, const std::string &queue_name)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto eit = _bindings.find(exchange_name);
            if (eit == _bindings.end() || eit->second.find(queue_name) == eit->second.end())
            {
                std::cout << "没找到!" << std::endl;
                return;
            }
            // 删除持久化
            auto qit = _bindings.find(exchange_name);
            if (qit->second[queue_name]->_durable)
                _mapper.Remove(exchange_name, queue_name);

            // 解除队列
            eit->second.erase(queue_name);
        }
        // 删除交换机的绑定
        void RemoveExchangeBindings(const std::string &exchange_name)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto eit = _bindings.find(exchange_name);
            if (eit == _bindings.end())
                return;
            _mapper.RemoveExchangeBindings(exchange_name);
            _bindings.erase(exchange_name);
        }

        void RemoveQueueBinding(const std::string &queue_name)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            for (auto it = _bindings.begin(); it != _bindings.end(); it++)
            {
                if (it->second.find(queue_name) != it->second.end())
                {

                    if (it->second.find(queue_name) != it->second.end())
                    {
                        it->second.erase(queue_name);
                        _mapper.RemoveQueueBindings(queue_name);
                    }
                }
            }
        }

        BindingPtr Getbinding(const std::string &exchange_name, const std::string &queue_name)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _bindings.find(exchange_name);
            if (it == _bindings.end())
                return BindingPtr();
            if (it->second.find(queue_name) != it->second.end())
                return it->second.find(queue_name)->second;
            return BindingPtr();
        }

        // 获取交换机的所有绑定队列
        QueueMap GetExchangeBindings(const std::string &exchange_name)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto eit = _bindings.find(exchange_name);
            if (eit == _bindings.end())
                return QueueMap();
            return eit->second;
        }

        bool IsExistsBinding(const std::string &exchange_name, const std::string &queue_name)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto eit = _bindings.find(exchange_name);
            if (eit == _bindings.end())
                return false;
            auto qit = _bindings[exchange_name].find(queue_name);
            if (qit == eit->second.end())
                return false;
            return true;
        }
        ssize_t Size()
        {
            std::unique_lock<std::mutex> lock(_mutex);
            ssize_t total = 0;
            for (auto it = _bindings.begin(); it != _bindings.end(); it++)
            {
                total += it->second.size();
            }
            return total;
        }

        void Clear()
        {
            std::unique_lock<std::mutex> lock(_mutex);
            _mapper.RemoveTable();
            _bindings.clear();
        }

    private:
        std::mutex _mutex;
        BindingMapper _mapper;
        BindingMap _bindings;
    };
}